using System.IO;
using UnityEngine;
using UnityEngine.UI;

namespace UnityEditor
{
    [CanEditMultipleObjects]
    [CustomEditor(typeof(GameObject))]
    internal class GameObjectInspectorEx : GameObjectInspector
    {
        public override void OnPreviewGUI(Rect r, GUIStyle background)
        {
            var targetGameObject = target as GameObject;
            var preview = EditorGUIUtility.GetIconForObject(targetGameObject);
            if (preview == null)
            {
                base.OnPreviewGUI(r, background);
                return;
            }
            GUI.DrawTexture(r, preview);
        }

        public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
        {
            var targetGameObject = target as GameObject;
            var preview = EditorGUIUtility.GetIconForObject(targetGameObject);
            if (preview == null)
                return null;
            //example.PreviewIcon must be a supported format: ARGB32, RGBA32, RGB24,
            // Alpha8 or one of float formats
            var tex = new Texture2D(width, height);
            EditorUtility.CopySerialized(preview, tex);
            return tex;
        }

        [MenuItem("Assets/GameDesigner/生成预制体预览图")]
        public static void CreatePreview()
        {
            const string cachePreviewPath = "CachePreviews";
            foreach (var targetGameObject in Selection.gameObjects)
            {
                var assetPath = AssetDatabase.GetAssetPath(targetGameObject);
                var guid = AssetDatabase.AssetPathToGUID(assetPath);
                if (string.IsNullOrEmpty(guid))
                {
                    Debug.LogError("选择的目标不是一个预制体");
                    return;
                }
                var rel_pathname = Path.Combine(cachePreviewPath, guid + ".png");
                var pathname = Path.Combine("Assets", rel_pathname);
                var preview = GetAssetPreview(targetGameObject);
                SaveTexture2D(preview as Texture2D, Path.Combine(Application.dataPath, rel_pathname));
                {
                    AssetDatabase.ImportAsset(pathname);
                    AssetDatabase.Refresh();
                    TextureImporter Importer = AssetImporter.GetAtPath(pathname) as TextureImporter;
                    Importer.textureType = TextureImporterType.Default;
                    TextureImporterPlatformSettings setting = Importer.GetDefaultPlatformTextureSettings();
                    setting.format = TextureImporterFormat.RGBA32;
                    setting.textureCompression = TextureImporterCompression.Uncompressed;
                    Importer.SetPlatformTextureSettings(setting);
                    Importer.mipmapEnabled = false;
                    AssetDatabase.ImportAsset(pathname);
                    AssetDatabase.Refresh();
                    Debug.Log("SaveTextureToPNG " + pathname);
                }
                preview = AssetDatabase.LoadAssetAtPath<Texture2D>(pathname);
                var targetGameObjectClone = PrefabUtility.InstantiatePrefab(targetGameObject) as GameObject;
                EditorGUIUtility.SetIconForObject(targetGameObjectClone, preview as Texture2D);
                PrefabUtility.ApplyPrefabInstance(targetGameObjectClone, InteractionMode.AutomatedAction);
                DestroyImmediate(targetGameObjectClone);
                AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
                AssetDatabase.Refresh();
            }
        }

        public static Texture GetAssetPreview(GameObject obj)
        {
            GameObject canvas_obj = null;
            var clone = Instantiate(obj);
            var cloneTransform = clone.transform;
            var cameraObj = new GameObject("render camera");
            var renderCamera = cameraObj.AddComponent<Camera>();
            renderCamera.backgroundColor = new Color(0f, 0f, 0f, 0f);
            renderCamera.clearFlags = CameraClearFlags.Color;
            renderCamera.cameraType = CameraType.SceneView;
            renderCamera.cullingMask = 1 << 21;
            renderCamera.nearClipPlane = -100;
            renderCamera.farClipPlane = 100;
            bool isUINode = false;
            if (cloneTransform is RectTransform rectTransform)
            {
                canvas_obj = new GameObject("render canvas", typeof(Canvas), typeof(CanvasScaler));
                var canvas = canvas_obj.GetComponent<Canvas>();
                cloneTransform.parent = canvas_obj.transform;
                cloneTransform.localPosition = Vector3.zero;
                canvas_obj.layer = 21;//放在21层，摄像机也只渲染此层的，避免混入了奇怪的东西
                LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform);
                var canvasScaler = canvas_obj.GetComponent<CanvasScaler>();
                canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
                canvasScaler.referenceResolution = rectTransform.sizeDelta;
                canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;
                canvas.renderMode = RenderMode.ScreenSpaceCamera;
                canvas.worldCamera = renderCamera;
                isUINode = true;
            }
            else cloneTransform.position = new Vector3(-1000, -1000, -1000);
            var all = clone.GetComponentsInChildren<Transform>();
            foreach (Transform trans in all)
                trans.gameObject.layer = 21;
            var bounds = GetBounds(clone);
            var Min = bounds.min;
            var Max = bounds.max;
            if (isUINode)
            {
                cameraObj.transform.position = new Vector3(0, 0, -10);
                cameraObj.transform.LookAt(Vector3.zero);
                renderCamera.orthographic = true;
                var width = Max.x - Min.x;
                var height = Max.y - Min.y;
                var max_camera_size = width > height ? width : height;
                renderCamera.orthographicSize = 5f;//预览图要尽量少点空白
            }
            else
            {
                cameraObj.transform.position = new Vector3((Max.x + Min.x) / 2f, (Max.y + Min.y) / 2f, Max.z + (Max.z - Min.z));
                var center = new Vector3(cloneTransform.position.x, (Max.y + Min.y) / 2f, cloneTransform.position.z);
                cameraObj.transform.LookAt(center);
                var angle = (int)(Mathf.Atan2((Max.y - Min.y) / 2, (Max.z - Min.z)) * 180 / 3.1415f * 2);
                renderCamera.fieldOfView = angle;
            }
            var texture = new RenderTexture(128, 128, 0, RenderTextureFormat.Default);
            renderCamera.targetTexture = texture;
            var tex = RTImage(renderCamera);
            DestroyImmediate(canvas_obj);
            DestroyImmediate(cameraObj);
            return tex;
        }

        private static Texture2D RTImage(Camera camera)
        {
            var currentRT = RenderTexture.active;
            RenderTexture.active = camera.targetTexture;
            camera.Render();
            var image = new Texture2D(camera.targetTexture.width, camera.targetTexture.height);
            image.ReadPixels(new Rect(0, 0, camera.targetTexture.width, camera.targetTexture.height), 0, 0);
            image.Apply();
            RenderTexture.active = currentRT;
            return image;
        }

        public static Bounds GetBounds(GameObject obj)
        {
            var Min = new Vector3(99999, 99999, 99999);
            var Max = new Vector3(-99999, -99999, -99999);
            var renders = obj.GetComponentsInChildren<MeshRenderer>();
            if (renders.Length > 0)
            {
                for (int i = 0; i < renders.Length; i++)
                {
                    if (renders[i].bounds.min.x < Min.x)
                        Min.x = renders[i].bounds.min.x;
                    if (renders[i].bounds.min.y < Min.y)
                        Min.y = renders[i].bounds.min.y;
                    if (renders[i].bounds.min.z < Min.z)
                        Min.z = renders[i].bounds.min.z;

                    if (renders[i].bounds.max.x > Max.x)
                        Max.x = renders[i].bounds.max.x;
                    if (renders[i].bounds.max.y > Max.y)
                        Max.y = renders[i].bounds.max.y;
                    if (renders[i].bounds.max.z > Max.z)
                        Max.z = renders[i].bounds.max.z;
                }
            }
            else
            {
                var rectTrans = obj.GetComponentsInChildren<RectTransform>();
                var corner = new Vector3[4];
                for (int i = 0; i < rectTrans.Length; i++)
                {
                    //获取节点的四个角的世界坐标，分别按顺序为左下左上，右上右下
                    rectTrans[i].GetWorldCorners(corner);
                    if (corner[0].x < Min.x)
                        Min.x = corner[0].x;
                    if (corner[0].y < Min.y)
                        Min.y = corner[0].y;
                    if (corner[0].z < Min.z)
                        Min.z = corner[0].z;

                    if (corner[2].x > Max.x)
                        Max.x = corner[2].x;
                    if (corner[2].y > Max.y)
                        Max.y = corner[2].y;
                    if (corner[2].z > Max.z)
                        Max.z = corner[2].z;
                }
            }
            var center = (Min + Max) / 2;
            var size = new Vector3(Max.x - Min.x, Max.y - Min.y, Max.z - Min.z);
            return new Bounds(center, size);
        }

        public static bool SaveTexture2D(Texture2D png, string save_file_name)
        {
            var bytes = png.EncodeToPNG();
            var directory = Path.GetDirectoryName(save_file_name);
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);
            File.WriteAllBytes(save_file_name, bytes);
            return true;
        }
    }
}